package com.atguigu.yygh.hosp.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.exception.YyghException;
import com.atguigu.yygh.hosp.mapper.ScheduleRepository;
import com.atguigu.yygh.hosp.service.DepartmentService;
import com.atguigu.yygh.hosp.service.HospitalService;
import com.atguigu.yygh.hosp.service.ScheduleService;
import com.atguigu.yygh.model.hosp.BookingRule;
import com.atguigu.yygh.model.hosp.Hospital;
import com.atguigu.yygh.model.hosp.Schedule;
import com.atguigu.yygh.vo.hosp.BookingScheduleRuleVo;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.hosp.ScheduleQueryVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.util.*;


/**
 * @author:ahang
 * @create:2022-09-03 0:32
 * @Description:
 */
@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private HospitalService hospitalService;

    @Autowired
    private DepartmentService departmentService;

    //更新号源数量
    @Override
    public void update(Schedule schedule) {
        schedule.setUpdateTime(new Date());
        scheduleRepository.save(schedule);
    }


    /**
     * 根据排班id获取预约下单数据
     * @param scheduleId
     * @return
     */
    @Override
    public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {
        //用来封装下单数据
        ScheduleOrderVo scheduleOrderVo = new ScheduleOrderVo();
        //查询排班
        Schedule schedule = scheduleRepository.findById(scheduleId).get();
        Hospital hospital = hospitalService.getByHoscode(schedule.getHoscode());
        BookingRule bookingRule = hospital.getBookingRule();//医院预约规则

        scheduleOrderVo.setHoscode(hospital.getHoscode());//mongodb中的医院编号
        scheduleOrderVo.setHosname(hospital.getHosname());//医院名称
        scheduleOrderVo.setDepcode(schedule.getDepcode());//科室编号
        scheduleOrderVo.setDepname(departmentService.getDepName(hospital.getHoscode(),schedule.getDepcode()));//科室名称
        scheduleOrderVo.setHosScheduleId(schedule.getHosScheduleId());//对应医院端排班的id
        scheduleOrderVo.setTitle(schedule.getTitle());//医生职称
        scheduleOrderVo.setReserveDate(schedule.getWorkDate());//安排日期
        scheduleOrderVo.setReserveTime(schedule.getWorkTime());//安排时间
        scheduleOrderVo.setAvailableNumber(schedule.getAvailableNumber());//剩余预约数
        scheduleOrderVo.setAmount(schedule.getAmount()); //挂号费

        DateTime quitTime = this.getDateTime(new DateTime(schedule.getWorkDate()).plusDays(bookingRule.getQuitDay()).toDate(), bookingRule.getQuitTime());
        scheduleOrderVo.setQuitTime(quitTime.toDate()); //退号时间

        DateTime startTime = this.getDateTime(new Date(), bookingRule.getReleaseTime());
        scheduleOrderVo.setStartTime(startTime.toDate()); //当天开始挂号时间

        DateTime stopTime = this.getDateTime(new Date(), bookingRule.getStopTime());
        scheduleOrderVo.setStopTime(stopTime.toDate()); //当天停止挂号时间

        DateTime endTime = this.getDateTime(new DateTime().plusDays(bookingRule.getCycle()).toDate(), bookingRule.getStopTime());
        scheduleOrderVo.setStopTime(endTime.toDate()); //挂号截止时间


        return scheduleOrderVo;
    }

    @Override
    public Map<String, Object> getBookingScheduleRule(Integer page, Integer limit, String hoscode, String depcode) {

        Hospital hospital = hospitalService.getByHoscode(hoscode);
        if(hoscode==null){
            throw new YyghException(20001,"医院不存在");
        }
        BookingRule bookingRule = hospital.getBookingRule();

        IPage<Date> listDate = this.getListDate(page, limit, bookingRule);
        List<Date> dateList = listDate.getRecords();


        //查询指定排班
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode).and("workDate").in(dateList);

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
                        .first("workDate").as("workDate")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber")
                        .count().as("docCount")
        );

        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class).getMappedResults();

        Map<Date,BookingScheduleRuleVo> map = new HashMap<>();
        bookingScheduleRuleVoList.forEach(bookingScheduleRuleVo -> {
            map.put(bookingScheduleRuleVo.getWorkDate(),bookingScheduleRuleVo);
        });


        // 找到不存在bookingRuleVo的日期
        //遍历后，每一个日期，都有对应的bookingScheduleRuleVo对象，存在bookingScheduleRuleVos集合中
        List<BookingScheduleRuleVo> bookingScheduleRuleVos = new ArrayList<>();
        for (int i = 0; i < dateList.size(); i++) {

            Date date = dateList.get(i);

            BookingScheduleRuleVo bookingScheduleRuleVo = map.get(date);

            if(bookingScheduleRuleVo==null){
                bookingScheduleRuleVo = new BookingScheduleRuleVo();
                bookingScheduleRuleVo.setDocCount(0);
                bookingScheduleRuleVo.setReservedNumber(-1);
                bookingScheduleRuleVo.setAvailableNumber(-1);
                bookingScheduleRuleVo.setWorkDate(date);
            }

            bookingScheduleRuleVo.setWorkDateMd(date);
            bookingScheduleRuleVo.setDayOfWeek(this.getDayOfWeek(new DateTime(date)));//星期


//            0：正常 1：即将放号 -1：当天已停止挂号


            long pages = listDate.getPages();//总页数
            if(page==pages && i == dateList.size()-1){
                bookingScheduleRuleVo.setStatus(1);
            }else{
                bookingScheduleRuleVo.setStatus(0);
            }


            // 当前是否已经停止挂号 ---  第一页的第一条数据
            if(page==1 && i==0 ){
                String stopTime = bookingRule.getStopTime();
                DateTime dateTimeStop = this.getDateTime(new Date(), stopTime);
                if(dateTimeStop.isBeforeNow()){
                    bookingScheduleRuleVo.setStatus(-1);
                }
            }


            bookingScheduleRuleVos.add(bookingScheduleRuleVo);
        }


        Map<String,Object> result = new HashMap<>();

        result.put("bookingScheduleList",bookingScheduleRuleVos);
        result.put("total",listDate.getTotal());//日期总记录数

        Map<String,Object> baseMap = new HashMap<>();


        baseMap.put("hosname", hospitalService.getHospName(hoscode));
        baseMap.put("bigname", departmentService.getDepartment(hoscode, depcode).getBigname());//大科室名称
        baseMap.put("depname", departmentService.getDepartment(hoscode, depcode).getDepname());//小科室名称
        baseMap.put("workDateString", new DateTime().toString("yyyy年MM月"));
        baseMap.put("releaseTime", bookingRule.getReleaseTime());
        baseMap.put("stopTime", bookingRule.getStopTime());

        result.put("baseMap",baseMap);


        return result;
    }

    @Override
    public IPage<Date> getListDate(int page, int limit, BookingRule bookingRule) {
        Integer cycle = bookingRule.getCycle();//预约周期 10

        String releaseTime = bookingRule.getReleaseTime();
        //今天放号时间所对应的DateTime对象
        DateTime dateTimeReleaseTime = this.getDateTime(new Date(),releaseTime);
        if(dateTimeReleaseTime.isBeforeNow()){
            //今天已经开始放号
            cycle++;  //最后--即将放号
        }

        //创建日期对象 （ yyyy-MM-dd ）            14    5
        // 1   5     0--4     5--9
        // (page-1)*limit    (page-1)*limit+limit

        List<Date> dateList = new ArrayList<>();

        for (int i = 0; i < cycle; i++) {
            DateTime dateTime = new DateTime().plusDays(i);
            String s = dateTime.toString("yyyy-MM-dd");
            Date date = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(s).toDate();
            dateList.add(date);
        }


        List<Date> pageList = new ArrayList<>();

        int start = (page-1)*limit;  // 14    5    5 5 4
        int end = (page-1)*limit + limit;   //15

        if(end > dateList.size()){
            end = dateList.size();
        }

        for (int i = start; i < end; i++) {
            Date date = dateList.get(i);
            pageList.add(date);
        }


        com.baomidou.mybatisplus.extension.plugins.pagination.Page<Date> datePage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, limit, dateList.size());
        datePage.setRecords(pageList);//当前页数据集合

        return datePage;
    }

    private DateTime getDateTime(Date date, String time) {
        //yyyy-MM-dd HH:mm   中间加一个空格
        String dateTime = new DateTime(date).toString("yyyy-MM-dd") +" "+ time;
        return DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(dateTime);
    }

    //根据id查询排班
    @Override
    public Schedule getById(String id) {
        Schedule schedule = scheduleRepository.findById(id).get();
        this.packSchedule(schedule);
        return schedule;
    }


    @Override
    public List<Schedule> getDetailSchedule(String hoscode, String depcode, String workDate) {

        List<Schedule> list = scheduleRepository.findScheduleByHoscodeAndDepcodeAndWorkDate(hoscode, depcode, new DateTime(workDate).toDate());

        // schedule -->param( hosname + depname + dayOfWeek )
        list.forEach(schedule -> {
            this.packSchedule(schedule);
        });

        return list;
    }

    private void packSchedule(Schedule schedule) {
        // schedule -->param( hosname + depname + dayOfWeek )

        String hospName = hospitalService.getHospName(schedule.getHoscode());
        String depName = departmentService.getDepName(schedule.getHoscode(), schedule.getDepcode());

        Date workDate = schedule.getWorkDate();
        String dayOfWeek = this.getDayOfWeek(new DateTime(workDate));

        schedule.getParam().put("hosname",hospName);
        schedule.getParam().put("depname",depName);
        schedule.getParam().put("dayOfWeek",dayOfWeek);

    }

    @Override
    public void save(Map<String, Object> paramMap) {
        //paramMap 转换department对象
        String paramMapString = JSONObject.toJSONString(paramMap);
        Schedule schedule = JSONObject.parseObject(paramMapString, Schedule.class);

        //根据医院编号 和 排班编号查询
        Schedule scheduleExist = scheduleRepository.
                getScheduleByHoscodeAndHosScheduleId(schedule.getHoscode(),schedule.getHosScheduleId());
        //判断
        if(scheduleExist==null){
            schedule.setCreateTime(new Date());
            schedule.setUpdateTime(new Date());
            schedule.setStatus(1);
        }else{
            schedule.setUpdateTime(new Date());
            schedule.setCreateTime(schedule.getCreateTime());
            schedule.setStatus(1);
        }
        scheduleRepository.save(schedule);
    }

    /**
     * 条件 + 分页
     */
    @Override
    public Page<Schedule> selectPage(Integer pageNum, Integer limit, ScheduleQueryVo scheduleQueryVo) {
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        //0为第一页
        Pageable pageable = PageRequest.of(pageNum-1, limit, sort);

        Schedule schedule = new Schedule();
        BeanUtils.copyProperties(scheduleQueryVo, schedule);

        //创建匹配器，即如何使用查询条件
        ExampleMatcher matcher = ExampleMatcher
                .matching() //构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式：模糊查询
                .withIgnoreCase(true); //改变默认大小写忽略方式：忽略大小写

        //创建实例
        Example<Schedule> example = Example.of(schedule, matcher);

        Page<Schedule> page = scheduleRepository.findAll(example, pageable);

        return page;
    }


    //实现方法
    @Override
    public void remove(String hoscode, String hosScheduleId) {
        Schedule schedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, hosScheduleId);
        if(null != schedule) {
            scheduleRepository.deleteById(schedule.getId());
        }
    }

    //查询排班日期
    @Override
    public Map<String, Object> getRuleSchedule(Long page, Long limit, String hoscode, String depcode) {
        //1.最终的返回值
        Map<String,Object> result = new HashMap<>();
        //2.条件 == 医院编号+科室编号===》排班==》分组
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);
        //3.根据工作日workData进行查询
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria), //对指定条件的数据进行聚合
                Aggregation.group("workDate")
                        //每一组（排班）提取一些数据 ---> BookingScheduleRuleVo
                        .first("workDate").as("workDate")
                        .count().as("docCount")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber"),
                Aggregation.sort(Sort.Direction.ASC, "workDate"),//按照日期排序
                Aggregation.skip((page - 1) * limit),
                Aggregation.limit(limit)
        );
        //4.使用MongoTemplate进行查询(Schedule.class --> 查询对象  BookingScheduleRuleVo.class --> 封装对象 )
        AggregationResults<BookingScheduleRuleVo> aggregate =
                mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        //5.调用get方法获取到分页后的集合
        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggregate.getMappedResults();
        //6.根据workDate,获取到对应的星期
        bookingScheduleRuleVoList.forEach(item->{
            Date workDate = item.getWorkDate();
            String dayOfWeek = getDayOfWeek(new DateTime(workDate));
            item.setDayOfWeek(dayOfWeek);
        });
        //----------------------------bookingScheduleRuleVoList 查询完毕
        //7.按照workDate进行分组-->获取日期总记录数（前端进行分页使用）
        Aggregation aggregationTotal = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
        );
        AggregationResults<BookingScheduleRuleVo> aggregate1 = mongoTemplate.aggregate(aggregationTotal, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> mappedResults = aggregate1.getMappedResults();
        int total = mappedResults.size();
        //----------------------日期总数--------
        //8.获取医院名称
        Map<String,Object> baseMap = new HashMap<>();
        baseMap.put("hosname",hospitalService.getHospName(hoscode));

        result.put("total",total);
        result.put("bookingScheduleRuleList",bookingScheduleRuleVoList);
        result.put("baseMap",baseMap);
        return result;
    }

    private String getDayOfWeek(DateTime workDate){
        int num = workDate.getDayOfWeek();
        // num = 1   星期一   2 星期二
        String dayOfWeek = "";

        switch (num){
            case DateTimeConstants.MONDAY:
                dayOfWeek="星期一";
                break;
            case DateTimeConstants.TUESDAY:
                dayOfWeek="星期二";
                break;
            case DateTimeConstants.WEDNESDAY:
                dayOfWeek="星期三";
                break;
            case DateTimeConstants.THURSDAY:
                dayOfWeek="星期四";
                break;
            case DateTimeConstants.FRIDAY:
                dayOfWeek="星期五";
                break;
            case DateTimeConstants.SATURDAY:
                dayOfWeek="星期六";
                break;
            case DateTimeConstants.SUNDAY:
                dayOfWeek="星期日";
                break;
            default:
                dayOfWeek="星期九";
        }
        return dayOfWeek;

    }
}
